package jinyilw.common;

import jinyilw.common.file.FileTools;
import jinyilw.common.log.LogTools;

import java.io.File;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * 获取特定操作系统信息和选项的类
 */
public class OSTools
{
	private static String userHomeDirectory;
	private static String preferencesDirectory;
	private static String appDataDirectory;
	private static String cacheDirectory;

	private static Double osVersion;

	private OSTools()
	{
	}

	/**
	 * 获得操作系统的枚举类
	 */
	public enum OSType
	{
		MacOS, Windows, Linux, Solaris, Other;

		private static OSType osType;

		public static OSType getOSType()
		{
			if (osType == null)
			{
				String osName = System.getProperty("os.name")
						.toLowerCase(Locale.ENGLISH);
				osType = osName.contains("win")
						? Windows
						: osName.contains("mac")
						|| osName.contains("os x")
						? MacOS
						: osName.contains("nix")
						|| osName.contains("nux")
						|| osName.contains("aix")
						? Linux
						: osName.contains("solaris")
						|| osName.contains("sunos")
						? Solaris
						: Other;
			}
			return osType;
		}

		public static final boolean isMac = getOSType() == MacOS;
		public static final boolean isWindows = getOSType() == Windows;
	}

	public static boolean isMac()
	{
		return OSType.isMac;
	}

	public static boolean isWindows()
	{
		return OSType.isWindows;
	}

	public static boolean isLinux()
	{
		return OSType.getOSType() == OSType.Linux;
	}

	public static boolean isSolaris()
	{
		return OSType.getOSType() == OSType.Solaris;
	}

	public static double getOSVersion()
	{
		if (osVersion == null)
		{
			try
			{
				String ver = System.getProperty("os.version");
				StringBuilder version = new StringBuilder();
				boolean firstPoint = true;
				for (int i = 0; i < ver.length(); i++)
				{
					if (ver.charAt(i) == '.')
					{
						if (firstPoint)
							version.append(ver.charAt(i));
						firstPoint = false;
					} else if (Character.isDigit(ver.charAt(i)))
						version.append(ver.charAt(i));
				}
				osVersion = Double.parseDouble(version.toString());
			} catch (NumberFormatException ex)
			{
				osVersion = 1.0;
			}
		}
		return osVersion;
	}

	public static boolean isMacOSXLeopardOrSuperior()
	{
		return OSType.isMac && !System.getProperty("os.version").startsWith("10.4");
	}

	public static boolean isMacOSXLionOrSuperior()
	{
		return OSType.isMac && compareVersions(System.getProperty("os.version"), "10.7") >= 0;
	}

	public static boolean isMacOSXYosemiteOrSuperior()
	{
		return OSType.isMac && compareVersions(System.getProperty("os.version"),
				"10.10") >= 0;
	}

	public static boolean isMacOSXHighSierraOrSuperior()
	{
		return OSType.isMac && compareVersions(System.getProperty("os.version"),
				"10.13") >= 0;
	}

	public static int compareVersions(String version1, String version2)
	{
		List<Object> version1Parts = splitVersion(version1);
		List<Object> version2Parts = splitVersion(version2);
		int i = 0;
		for (; i < version1Parts.size() || i < version2Parts.size(); i++)
		{
			Object version1Part = i < version1Parts.size()
					? convertPreReleaseVersion(version1Parts.get(i))
					: BigInteger.ZERO;
			Object version2Part = i < version2Parts.size()
					? convertPreReleaseVersion(version2Parts.get(i))
					: BigInteger.ZERO;
			if (version1Part.getClass() == version2Part.getClass())
			{
				int comparison = ((Comparable) version1Part)
						.compareTo(version2Part);
				if (comparison != 0)
					return comparison;
			} else if (version1Part instanceof String)
			{
				return ((BigInteger) version2Part).signum() > 0 ? -1 : 1;
			} else
			{
				return ((BigInteger) version1Part).signum() > 0 ? 1 : -1;
			}
		}
		return 0;
	}

	private static List<Object> splitVersion(String version)
	{
		List<Object> versionParts = new ArrayList<>();
		StringBuilder subPart = new StringBuilder();
		for (String part : version.split("\\p{Punct}|\\s"))
		{
			for (int i = 0; i < part.length(); )
			{
				subPart.setLength(0);
				char c = part.charAt(i);
				if (Character.isDigit(c))
				{
					for (; i < part.length()
							&& Character.isDigit(c = part.charAt(i)); i++)
					{
						subPart.append(c);
					}
					versionParts.add(new BigInteger(subPart.toString()));
				} else
				{
					for (; i < part.length()
							&& !Character.isDigit(c = part.charAt(i)); i++)
					{
						subPart.append(c);
					}
					versionParts.add(subPart.toString());
				}
			}
		}
		return versionParts;
	}

	private static Object convertPreReleaseVersion(Object versionPart)
	{
		if (versionPart instanceof String)
		{
			String versionPartString = (String) versionPart;
			if ("alpha".equalsIgnoreCase(versionPartString))
			{
				return new BigInteger("-3");
			} else if ("beta".equalsIgnoreCase(versionPartString))
			{
				return new BigInteger("-2");
			} else if ("rc".equalsIgnoreCase(versionPartString))
			{
				return new BigInteger("-1");
			}
		}
		return versionPart;
	}

	public static File getPreferencesFile(String appName)
	{
		String userHomeDirectory = System.getProperty("user.home");
		String preferencesPath;
		if (OSType.isWindows)
			preferencesPath = System.getenv("APPDATA") + "/." + appName;
		else if (OSType.isMac)
			preferencesPath = userHomeDirectory + "/Library/Preferences/."
					+ appName;
		else if (isLinux())
		{
			preferencesPath = System.getenv("XDG_CONFIG_HOME");
			if (preferencesPath != null && !preferencesPath.trim().isEmpty())
				preferencesPath = preferencesPath + "/." + appName;
			else
				preferencesPath = userHomeDirectory + "/.config/." + appName;
		} else
			preferencesPath = userHomeDirectory + "/." + appName;

		return FileTools.createFile(preferencesPath);
	}

	public static String getUserHomeDirectory()
	{
		return userHomeDirectory;
	}

	public static String getPreferencesDirectory()
	{
		return preferencesDirectory;
	}

	public static String getAppDataDirectory()
	{
		return appDataDirectory;
	}

	public static String getCacheDirectory()
	{
		return cacheDirectory;
	}

	public static void setOSSpecificDirectories(String appName)
	{
		userHomeDirectory = System.getProperty("user.home");
		if (OSType.isWindows)
		{
			preferencesDirectory = System.getenv("APPDATA") + "/" + appName
					+ "/";
			appDataDirectory = System.getenv("APPDATA") + "/" + appName + "/";
			cacheDirectory = System.getProperty("java.io.tmpdir") + "/"
					+ appName + "/";
		} else if (OSType.isMac)
		{
			preferencesDirectory = userHomeDirectory + "/Library/Preferences/"
					+ appName + "/";
			appDataDirectory = userHomeDirectory + "/Library/" + appName + "/";
			cacheDirectory = System.getProperty("java.io.tmpdir") + "/"
					+ appName + "/";
		} else if (isLinux())
		{
			preferencesDirectory = System.getenv("XDG_CONFIG_HOME");
			if (preferencesDirectory != null
					&& !preferencesDirectory.trim().isEmpty())
				preferencesDirectory = preferencesDirectory + "/" + appName
						+ "/";
			else
				preferencesDirectory = userHomeDirectory + "/.config/" + appName
						+ "/";
			appDataDirectory = System.getenv("XDG_DATA_HOME");
			if (appDataDirectory != null && !appDataDirectory.trim().isEmpty())
				appDataDirectory = appDataDirectory + "/" + appName + "/";
			else
				appDataDirectory = userHomeDirectory + "/.local/share/"
						+ appName + "/";
			cacheDirectory = System.getenv("XDG_CACHE_HOME");
			if (cacheDirectory != null && !cacheDirectory.trim().isEmpty())
				cacheDirectory = cacheDirectory + "/" + appName + "/";
			else
				cacheDirectory = userHomeDirectory + "/.cache/" + appName + "/";
		} else
			preferencesDirectory = appDataDirectory = cacheDirectory = userHomeDirectory
					+ "/" + appName + "/";

		if (FileTools.createDirectory(preferencesDirectory) == null)
		{
			File file = FileTools
					.createDirectory(userHomeDirectory + "/" + appName + "/");
			if (file != null)
				preferencesDirectory = file.getPath();
			else
			{
				file = new File("");
				if (file.canWrite())
					preferencesDirectory = file.getPath();
				else
				{
					LogTools.error("Can't access directory: " + userHomeDirectory + "/"
							+ appName + "/");
					System.exit(-1);
				}
			}
		}
		if (FileTools.createDirectory(appDataDirectory) == null)
			appDataDirectory = preferencesDirectory;
		if (FileTools.createDirectory(cacheDirectory) == null)
			cacheDirectory = preferencesDirectory;
	}
}
